
using System.Runtime.InteropServices;

namespace PlexMediaLinker
{
    // 扫描和硬链接操作封装
    public static class Scanner
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes);

        public static readonly string[] MediaExts = [".mkv", ".mp4", ".avi"];
        public static readonly string[] SubtitleExts = [".srt", ".ass"];

        public static bool IsMediaOrSubtitle(string path)
        {
            var ext = Path.GetExtension(path).ToLower();
            return MediaExts.Contains(ext) || SubtitleExts.Contains(ext);
        }

        public static bool ShouldProcessFile(string path, GroupConfig cfg)
        {
            if (cfg.FileFilter == FileFilterMode.MediaOnly)
                return IsMediaOrSubtitle(path);
            else
                return true; // 处理所有文件
        }

        // 遍历源目录所有文件，包含已经是硬链接的文件
        public static void ScanAndLink(GroupConfig cfg)
        {
            Logger.WriteScan($"[{cfg.Name}] 开始扫描源目录：{cfg.Source}");
            var all = Directory.EnumerateFiles(cfg.Source, "*.*", SearchOption.AllDirectories)
                               .Where(f => ShouldProcessFile(f, cfg))
                               .ToList();
            Logger.WriteScan($"[{cfg.Name}] 发现 {all.Count} 个文件");
            foreach (var file in all) ProcessFile(file, cfg);
            Logger.WriteScan($"[{cfg.Name}] 扫描处理完成");
        }

        public static void ProcessFile(string srcPath, GroupConfig cfg)
        {
            if (cfg.Mode == SyncMode.HardLink)
                LinkFile(srcPath, cfg);
            else
                CopyFile(srcPath, cfg);
        }

        public static void CopyFile(string srcPath, GroupConfig cfg)
        {
            try
            {
                string dstPath;
                if (cfg.Structure == StructureMode.Flatten)
                {
                    dstPath = Path.Combine(cfg.Target, Path.GetFileName(srcPath));
                }
                else
                {
                    var relativePath = Path.GetRelativePath(cfg.Source, srcPath);
                    dstPath = Path.Combine(cfg.Target, relativePath);
                }

                var dstDir = Path.GetDirectoryName(dstPath);
                if (!Directory.Exists(dstDir))
                    Directory.CreateDirectory(dstDir);

                bool needCopy = false;
                if (!File.Exists(dstPath))
                {
                    needCopy = true;
                }
                else
                {
                    var srcInfo = new FileInfo(srcPath);
                    var dstInfo = new FileInfo(dstPath);
                    if (srcInfo.Length != dstInfo.Length || srcInfo.LastWriteTimeUtc > dstInfo.LastWriteTimeUtc)
                    {
                        needCopy = true;
                    }
                }

                if (needCopy)
                {
                    File.Copy(srcPath, dstPath, true);
                    Logger.WriteCopy($"[{cfg.Name}] 复制文件: {Path.GetFileName(srcPath)} -> {dstPath}");
                }
            }
            catch (Exception ex)
            {
                Logger.WriteError($"[{cfg.Name}] 复制文件失败: {ex.Message}");
            }
        }

        public static void RemoveDeadLinks(GroupConfig cfg)
        {
            Logger.WriteClean($"[{cfg.Name}] 开始清理失效文件");
            var sourceFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            foreach (var src in Directory.EnumerateFiles(cfg.Source, "*.*", SearchOption.AllDirectories))
            {
                if (!ShouldProcessFile(src, cfg)) continue;
                string key;
                if (cfg.Structure == StructureMode.Flatten)
                {
                    key = Path.GetFileName(src);
                }
                else
                {
                    key = Path.GetRelativePath(cfg.Source, src);
                }
                sourceFiles.Add(key);

            }
            Logger.WriteClean($"[{cfg.Name}] 源文件名集合共 {sourceFiles.Count} 个文件");

            int removedCount = 0;
            foreach (var dst in Directory.EnumerateFiles(cfg.Target, "*.*", SearchOption.AllDirectories))
            {
                string key;
                if (cfg.Structure == StructureMode.Flatten)
                {
                    key = Path.GetFileName(dst);
                }
                else
                {
                    key = Path.GetRelativePath(cfg.Target, dst);
                }
                bool needDelete = false;

                if (!sourceFiles.Contains(key))
                {
                    // 源中无此文件，直接删除
                    needDelete = true;
                }
                // else if (cfg.Mode == SyncMode.Copy)
                // {
                //     // 复制模式下，校验内容（如大小）
                //     var srcInfo = sourceFileInfo[key];
                //     var dstInfo = new FileInfo(dst);
                //     if (srcInfo.Length != dstInfo.Length || srcInfo.LastWriteTimeUtc > dstInfo.LastWriteTimeUtc)
                //     {
                //         Logger.WriteClean($"[{cfg.Name}] 目标文件内容不一致，删除以便重新同步: {key}");
                //         needDelete = true;
                //     }
                // }
                if (needDelete)
                {
                    Logger.WriteClean($"[{cfg.Name}] 删除目标文件: {key}");
                    try
                    {
                        // 尝试去除只读属性
                        var fi = new FileInfo(dst);
                        if (fi.IsReadOnly) fi.IsReadOnly = false;
                        File.Delete(dst);
                        removedCount++;
                    }
                    catch (Exception ex)
                    {
                        Logger.WriteError($"[{cfg.Name}] 删除失败: {ex.Message}");
                    }
                }

            }
            int removedDirCount = 0;
            if (cfg.Structure == StructureMode.Preserve)
            {
                // 递归删除空目录
                removedDirCount = CleanEmptyDirectories(cfg.Target, cfg, true);
            }
            Logger.WriteClean($"[{cfg.Name}] 清理完成，共删除 {removedCount} 个失效文件，{removedDirCount} 个空目录");
        }

        public static int CleanEmptyDirectories(string path, GroupConfig cfg, bool isRoot = true)
        {
            int count = 0;
            try
            {
                if (!Directory.Exists(path)) return 0;

                // 先清理所有子目录
                foreach (var subDir in Directory.GetDirectories(path))
                {
                    count += CleanEmptyDirectories(subDir, cfg, false);
                }

                // 只有非根目录才允许删除
                if (!isRoot && !Directory.EnumerateFileSystemEntries(path).Any())
                {
                    Directory.Delete(path);
                    Logger.WriteClean($"[{cfg.Name}] 删除空目录: {path}");
                    count++;
                }
            }
            catch (Exception ex)
            {
                Logger.WriteError($"[{cfg.Name}] 清理空目录失败: {ex.Message}");
            }
            return count;
        }


        public static void LinkFile(string srcPath, GroupConfig cfg)
        {
            try
            {
                string dstPath;
                if (cfg.Structure == StructureMode.Flatten)
                {
                    dstPath = Path.Combine(cfg.Target, Path.GetFileName(srcPath));
                }
                else
                {
                    var relativePath = Path.GetRelativePath(cfg.Source, srcPath);
                    dstPath = Path.Combine(cfg.Target, relativePath);
                }

                var dstDir = Path.GetDirectoryName(dstPath);
                if (!string.IsNullOrEmpty(dstDir) && !Directory.Exists(dstDir))
                {
                    Logger.WriteClean($"[{cfg.Name}] 创建目标目录: {dstDir}");
                    Directory.CreateDirectory(dstDir);
                }

                if (!File.Exists(dstPath))
                {
                    if (!CreateHardLink(dstPath, srcPath, IntPtr.Zero))
                    {
                        var err = Marshal.GetLastWin32Error();
                        Logger.WriteError($"[{cfg.Name}] 创建硬链接失败: {Path.GetFileName(srcPath)} (错误码 {err})");
                    }
                    else
                    {
                        Logger.WriteLine($"[{cfg.Name}] 创建硬链接: {Path.GetFileName(srcPath)} -> {dstPath}");
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.WriteError($"[{cfg.Name}] 硬链接操作失败: {ex.Message}");
            }
        }

        public static void DeleteLink(string srcPath, GroupConfig cfg)
        {
            string dstPath;
            if (cfg.Structure == StructureMode.Flatten)
            {
                dstPath = Path.Combine(cfg.Target, Path.GetFileName(srcPath));
            }
            else
            {
                var relativePath = Path.GetRelativePath(cfg.Source, srcPath);
                dstPath = Path.Combine(cfg.Target, relativePath);
            }

            if (File.Exists(dstPath))
            {
                File.Delete(dstPath);
                Logger.WriteDelete($"[{cfg.Name}] 删除硬链接: {Path.GetFileName(srcPath)}");
            }
        }

        public static void DeleteFile(string srcPath, GroupConfig cfg)
        {
            if (cfg.Mode == SyncMode.HardLink)
                DeleteLink(srcPath, cfg);
            else
                DeleteCopiedFile(srcPath, cfg);
        }

        public static void DeleteCopiedFile(string srcPath, GroupConfig cfg)
        {
            string dstPath;
            if (cfg.Structure == StructureMode.Flatten)
            {
                dstPath = Path.Combine(cfg.Target, Path.GetFileName(srcPath));
            }
            else
            {
                var relativePath = Path.GetRelativePath(cfg.Source, srcPath);
                dstPath = Path.Combine(cfg.Target, relativePath);
            }

            if (File.Exists(dstPath))
            {
                File.Delete(dstPath);
                Logger.WriteDelete($"[{cfg.Name}] 删除文件: {Path.GetFileName(srcPath)}");
            }
        }
    }
}
